#include "TableOrder.h"
#include "Index.h"
#include <boost/thread/lock_guard.hpp>

class IndexOrderApplyTime : public StringIndex{
public:
    IndexOrderApplyTime()
    :StringIndex("applyTime")
    {
    }
    virtual ~IndexOrderApplyTime(){
    }

    int parseKey(const char * value, std::string & outKey) override {
        uint8_t len = value[0];
        outKey.assign(value+1, len);
        return 0;
    }
};


#include  "HashContainer.h"


TableOrder::TableOrder()
: Table("order")
, data(new HashContainer())
{
}
TableOrder::~TableOrder(){
    if(data != nullptr){
        delete data;
    }
}

int TableOrder::init() {
    boost::lock_guard<boost::mutex> guard(mutex);

    Index * applyTime = new IndexOrderApplyTime();
    indexes.insert(std::make_pair(applyTime->getName(), applyTime));
    return 0; 
}
void TableOrder::deinit() {
    boost::lock_guard<boost::mutex> guard(mutex);

    clear();
}

void TableOrder::clear(){
    {
        auto it = data->iterator();
        while(it->hasNext()){
            delete it->next().getValue();
        }
    }
    {
        auto it = indexes.begin();
        for(; it != indexes.end(); ++it){
            delete it->second;
        }
    }
}

int TableOrder::insert(const int key, const char * value) {
    boost::lock_guard<boost::mutex> guard(mutex);
    //indexes insert
    {
        auto it = indexes.begin();
        for(; it != indexes.end(); ++it){
            std::string indexKey;
            if(0 == it->second->parseKey(value, indexKey)){
                it->second->insert(indexKey, key);
            }
            else{
                //Error: parseKey for index error: index->getName();
                return -1;
            }
        }
    }
    // data insert
    {
        data->put(key, value);
    }
    return -1;
}
int TableOrder::update(const int key, const char * value) {
    boost::lock_guard<boost::mutex> guard(mutex);
    {
        auto it = indexes.begin();
        for(; it != indexes.end(); ++it){
            std::string indexKey;
            if(0 == it->second->parseKey(value, indexKey)){
                if( 0 != it->second->update(indexKey, key)){
                    return -1;
                }
            }
            else{
                //ParseKey  of index (name) error
                return -1;
            }
        }
    }
    
    //data update
    {
        data->put(key, value);
    }
    return -1;
}
int TableOrder::remove(const int key) {
    boost::lock_guard<boost::mutex> guard(mutex);
    auto value = data->get(key);
    if(value != nullptr){ // find key 
        auto indexIt = indexes.begin();
        for(; indexIt != indexes.end(); ++indexIt){
            std::string indexKey;
            if(0 == indexIt->second->parseKey(value, indexKey)){
                if( 0 != indexIt->second->remove(indexKey, key)){
                    return -1;
                }
            }
            else{
                //ParseKey  of index (name) error
                return -1;
            }
        }
        data->remove(key);
    }
    return 0;
}


const char * TableOrder::find(const int key) const {
    boost::lock_guard<boost::mutex> guard(mutex);
    return data->get(key);
}
Records * TableOrder::rangeScan(int offset, int count, bool isAscend, const std::string & indexName) const{
    boost::lock_guard<boost::mutex> guard(mutex);
    auto it = indexes.find(indexName);
    if(it != indexes.end()){
        if(nullptr != it->second){
            auto pkArray = it->second->rangeScan(offset, count, isAscend);
            if(pkArray != nullptr){
                Records * ret = new Records();
                for(auto e : *pkArray){
                    ret->pushBack(e, data->get(e));
                }
                return ret;
            }
            return nullptr;
        }
        //Error index(name) is nullptr
    }
    //Error no this index(name)
    return nullptr;
}

std::string TableOrder::toString() const {
    boost::lock_guard<boost::mutex> guard(mutex);
    std::ostringstream oss;

    oss << "TableOrder: "
        << " size:" << data->size() << std::endl;
    oss << " Data:";
    auto it = data->iterator();
    while(it->hasNext()){
        auto kv = it->next();
        oss << "\tk:" << kv.getKey()  << " v:" << std::hex << (uint64_t)(kv.getValue()) << std::dec <<std::endl;
    }
    oss << std::endl;

    oss << " Index:";
    auto indexIt = indexes.begin();
    for(; indexIt != indexes.end(); ++indexIt){
        oss << indexIt->second->toString() << std::endl;
    }
    return oss.str();
}
int TableOrder::totalCount() { 
    return data->size();
}



#include "Util.h"

void functionTest(){
    TableOrder order;
    order.init();

    order.insert(1, Util::make("hello\0"));
    order.insert(2, Util::make("hell1\0"));
    order.insert(3, Util::make("hell2\0"));
    order.insert(4, Util::make("hello\0"));
    order.insert(5, Util::make("hell1\0"));
    order.insert(6, Util::make("hello\0"));
    order.insert(7, Util::make("hell2\0"));

    std::cout << order.toString() << std::endl;

    auto func = [&order](int offset, int count, bool isAscend, const std::string & indexName){
        std::cout << "rangeScan:("<< offset<<","<<count<<","<<isAscend<<","<<indexName <<")";
        auto records = order.rangeScan(offset, count, isAscend, indexName);
        if(records != nullptr){
            std::cout << records->toString() << std::endl;
            delete records;
        }
        else{
            std::cout << "records is nullptr" << std::endl;
        }
    };
    func(0, 2, true, "applyTime");
    func(0, 7, true, "applyTime");

    func(0, 0, true, "applyTime");
    func(1, 1, true, "applyTime");
    func(2, 1, true, "applyTime");
    func(2, 0, true, "applyTime");

    func(3, 1, true, "applyTime");
    func(4, 1, true, "applyTime");

    func(5, 1, true, "applyTime");
    func(6, 1, true, "applyTime");

    func(7, 1, true, "applyTime");
    func(7, 0, true, "applyTime");

    func(2, 10, true, "applyTime");

    // desc orderApplyTime RangeScan
    func(0, 2, false, "applyTime");
    func(0, 7, false, "applyTime");

    func(0, 0, false, "applyTime");
    func(1, 1, false, "applyTime");
    func(2, 1, false, "applyTime");
    func(2, 0, false, "applyTime");

    func(3, 1, false, "applyTime");
    func(4, 1, false, "applyTime");

    func(5, 1, false, "applyTime");
    func(6, 1, false, "applyTime");

    func(7, 1, false, "applyTime");
    func(7, 0, false, "applyTime");

    func(2, 10, false, "applyTime");

    order.deinit();

}


#include <chrono>
void capacityTest(){
    TableOrder order;
    order.init();

    int count = 10*1000*1000;
    const char * str = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\0";

    //const char * str = "hello";
    //int count = 10;
    for(int i = 0; i < count; ++i){
        order.insert(i, Util::make(str));
    }



    std::cout << "data insert end" << std::endl;

    auto start = std::chrono::system_clock::now();

    auto func = [&order](int offset, int count, bool isAscend, const std::string & indexName){
        std::cout << "rangeScan:("<< offset<<","<<count<<","<<isAscend<<","<<indexName <<")";
        auto records = order.rangeScan(offset, count, isAscend, indexName);
        if(records != nullptr){
            std::cout << records->toString() << std::endl;
            delete records;
        }
        else{
            std::cout << "records is nullptr" << std::endl;
        }
    };
    func(100*10000, 7, false, "applyTime");
    auto end = std::chrono::system_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    std::cout << double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den
			<< "second" << std::endl;

    std::cout << "data scan end" << std::endl;
    order.deinit();
}
void TableOrder::test(){
    functionTest();
    //capacityTest();
}
